/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.analyzer;

import java.lang.reflect.Constructor;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGConstructorList;
import org.jfab.core.collection.CGParameterList;
import org.jfab.core.collection.CGTypeList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.ImportMap;
import org.jfab.core.collection.MethodBodyList;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.collection.ParameterNameList;
import org.jfab.core.factory.CGAnnotationFactory;
import org.jfab.core.factory.CGConstructorFactory;
import org.jfab.core.factory.CGParameterFactory;
import org.jfab.core.factory.CGTypeFactory;
import org.jfab.core.factory.StringFactory;
import org.jfab.core.model.CGConstructor;
import org.jfab.core.model.CGTypeUtilities;
import org.jfab.core.model.TypeFactory;

/**
 * Provides a constructor analyzer.
 */
public final class ConstructorAnalyzer extends
        AbstractAnalyzer<CGConstructor, Constructor<?>>
{
    /** Constructor factory. */
    private final CGConstructorFactory _constructorFactory;

    /** Method analysis utilities. */
    private final MethodAnalysisUtilities _methodAnalysisUtilities;

    /** Parameter factory. */
    private final CGParameterFactory _parameterFactory;

    /**
     * Construct this object with the given parameters.
     * 
     * @param typeFactory Type factory.
     * @param cgTypeUtils Code generator type utilities. (required)
     * @param cgTypeFactory Code generator type factory.
     * @param stringFactory String factory.
     * @param annotationFactory Annotation factory.
     * @param comparator Comparator.
     * @param parameterFactory Parameter factory.
     * @param methodAnalysisUtilities Method analysis utilities.
     * @param constructorFactory Constructor factory.
     */
    public ConstructorAnalyzer(final TypeFactory typeFactory,
            final CGTypeUtilities cgTypeUtils,
            final CGTypeFactory cgTypeFactory,
            final StringFactory stringFactory,
            final CGAnnotationFactory annotationFactory,
            final Comparator<CGConstructor> comparator,
            final CGParameterFactory parameterFactory,
            final MethodAnalysisUtilities methodAnalysisUtilities,
            final CGConstructorFactory constructorFactory)
    {
        super(typeFactory, cgTypeUtils, cgTypeFactory, stringFactory,
                annotationFactory, comparator);

        _parameterFactory = parameterFactory;
        _methodAnalysisUtilities = methodAnalysisUtilities;
        _constructorFactory = constructorFactory;
    }

    @Override
    public final CGConstructor analyze(final Constructor<?> constructor,
            final String javaSource)
    {
        Validate.notNull(constructor, "constructor is null");

        final int parameterOffset = 0;

        return analyze(constructor, javaSource, parameterOffset);
    }

    /**
     * Analyze the given object and return the result.
     * 
     * @param constructor Source object.
     * @param javaSource Java source code.
     * @param parameterOffset Parameter offset.
     * 
     * @return a new CGObject.
     */
    public final CGConstructor analyze(final Constructor<?> constructor,
            final String javaSource, final int parameterOffset)
    {
        CGTypeList referencedTypes = null;
        CommentList commentLines = null;
        CGAnnotationList annotations = null;
        final ModifierList modifiers = new ModifierList(
                constructor.getModifiers());
        final String className = constructor.getDeclaringClass()
                .getSimpleName();
        final CGTypeList parameterTypes = createListType(
                constructor.getParameterTypes(), parameterOffset);
        ParameterNameList parameterNames = null;
        final CGTypeList throwsTypes = createListType(constructor
                .getGenericExceptionTypes());
        MethodBodyList methodBodyLines = null;

        if (javaSource != null)
        {
            final ImportMap importMap = analyzeImports(javaSource);

            if (!importMap.isEmpty())
            {
                referencedTypes = new CGTypeList();
                referencedTypes.addAll(importMap.values());
            }

            final boolean isInterface = false;
            final int constructorIndex = locateMethod(javaSource, className,
                    parameterTypes, throwsTypes, isInterface);

            if (constructorIndex >= 0)
            {
                final String preMethod = javaSource.substring(0,
                        constructorIndex);
                commentLines = parseCommentLines(preMethod);
                annotations = getAnnotations(preMethod);

                final String postMethod = javaSource.substring(constructorIndex
                        + className.length());
                final MethodAnalysisUtilities methodUtils = getMethodAnalysisUtilities();
                parameterNames = methodUtils.parseParameterNames(postMethod);
                methodBodyLines = methodUtils.parseMethodBodyLines(postMethod);
            }
        }

        if (parameterNames == null)
        {
            parameterNames = new ParameterNameList();
        }

        final CGParameterList parameters = getParameterFactory()
                .createParameters(parameterTypes, parameterNames);

        return getConstructorFactory().createConstructor(referencedTypes,
                commentLines, annotations, modifiers, className, parameters,
                throwsTypes, methodBodyLines);
    }

    /**
     * @param objects Objects.
     * @param javaSource Java source.
     * @param parameterOffset Parameter offset.
     * 
     * @return a new list of code generator fields.
     */
    public final CGConstructorList analyze(final Constructor<?>[] objects,
            final String javaSource, final int parameterOffset)
    {
        final Comparator<CGConstructor> comparator = getComparator();
        final Set<CGConstructor> set = new TreeSet<CGConstructor>(comparator);

        for (final Constructor<?> object : objects)
        {
            set.add(analyze(object, javaSource, parameterOffset));
        }

        final CGConstructorList answer = new CGConstructorList(set);

        return answer;
    }

    /**
     * @return the constructorFactory
     */
    private CGConstructorFactory getConstructorFactory()
    {
        return _constructorFactory;
    }

    /**
     * @return the methodAnalysisUtilities
     */
    private MethodAnalysisUtilities getMethodAnalysisUtilities()
    {
        return _methodAnalysisUtilities;
    }

    /**
     * @return the parameterFactory
     */
    private CGParameterFactory getParameterFactory()
    {
        return _parameterFactory;
    }
}
